﻿#region Header

// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------

#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using System.Reflection;

#endregion

namespace ScrumTable.Common.FileSystem
{
    /// <summary>
    /// Represents a class for file handling. Each file is linked with a
    /// weak reference into the object tree. When the main handle is lost,
    /// the files will be deleted on the file system level.
    /// </summary>
    public class FilePool
    {
        #region Declarations

        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the default file pool instance.
        /// </summary>
        public static readonly FilePool Default = new FilePool();

        private readonly List<WeakReference> _files = new List<WeakReference>();

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------
        
        #endregion

        #region Constructors / Destructor

        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new FilePool instance. This constructor should be used
        /// in derived classes only.
        /// </summary>
        protected FilePool()
        {
        }

        #endregion

        #region Methods

        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new file pool entry from the given url. The file will be
        /// deleted if the file pool item isn't referenced from an object path.
        /// </summary>
        /// <param name="source">Specifies the file to handle in the pool.</param>
        /// <returns>Returns the file pool handle which should be hold as long you'd like to hold the file.</returns>
        public FilePoolHandle CreateFrom(Uri source)
        {
            PreCondition.AssertNotNull(source, "source");

            FileInfo sourceFile = new FileInfo(source.ToLocalPath());
            if (!sourceFile.Exists) throw new FileNotFoundException(source.ToLocalPath());
            return CreateWeakFile(sourceFile, false);
        }

        /// <summary>
        /// Creates a new file pool entry from the given url. This procedure copies the
        /// source file into a temporary directory (%TEMP%) and uses the given destination 
        /// file name to store the copy. The read only flags will be deleted.
        /// </summary>
        /// <param name="source">Specifies the file to handle in the pool.</param>
        /// <param name="destinationFileName">Specifies the destination file name in the temp directory.</param>
        /// <returns>Returns the file pool handle which should be hold as long you'd like to hold the file.</returns>
        public FilePoolHandle CreateInTempFrom(Uri source, string destinationFileName)
        {
            return CreateInTempFrom(
                source,
                Guid.NewGuid().ToString("D"),
                destinationFileName);
        }

        /// <summary>
        /// Creates a new file pool entry from the given url. This procedure copies the
        /// source file into a temporary directory (%TEMP%) and uses the given destination 
        /// file name to store the copy. The read only flags will be deleted.
        /// </summary>
        /// <param name="source">Specifies the file to handle in the pool.</param>
        /// <param name="randomKey"></param>
        /// <param name="destinationFileName">Specifies the destination file name in the temp directory.</param>
        /// <returns>Returns the file pool handle which should be hold as long you'd like to hold the file.</returns>
        public FilePoolHandle CreateInTempFrom(Uri source, string randomKey, string destinationFileName)
        {
            PreCondition.AssertNotNull(source, "source");
            PreCondition.AssertNotNullOrEmpty(randomKey, "randomKey");
            PreCondition.AssertNotNullOrEmpty(destinationFileName, "destinationFileName");

            FileInfo sourceFile = new FileInfo(source.ToLocalPath());
            if (!sourceFile.Exists) throw new FileNotFoundException(source.ToLocalPath());

            DirectoryInfo tempDir = DataFolderUtil.CreateRandomFolderInTemp(randomKey);
            string newFilePath = Path.Combine(tempDir.FullName, destinationFileName);

            // clone file on file system level);
            FileInfo sourceFileCopy = sourceFile.CopyTo(newFilePath);
            sourceFileCopy.IsReadOnly = false;

            return CreateWeakFile(sourceFileCopy, true);
        }

        private FilePoolHandle CreateWeakFile(FileInfo file, bool parentDirIsComposed)
        {
            FilePoolHandle newFile = new FilePoolHandle(file.FullName, parentDirIsComposed);

            lock (this)
            {
                bool emptyFound = false;

                // try to reuse an existing weak reference
                for (int i = 0; i < _files.Count; ++i )
                {
                    if (!_files[i].IsAlive)
                    {
                        _files[i] = new WeakReference(newFile);
                        emptyFound = true;
                    }
                }

                // create a new weak reference, if there is no weak ref available
                if (!emptyFound)
                {
                    // register file in local pool
                    _files.Add(new WeakReference(newFile));
                }
            }
            return newFile;
        }

        #endregion

        #region Events

        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}